// Copyright (c) 2011, Marc-Andre Ferland - madrang@gmail.com
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the The Warrent Team nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//     * This software is free for non-commercial use. You may not use this
//       software, in whole or in part, in support of any commercial product
//       without the express consent of the author.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE WARRENT TEAM BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using System;
using System.Runtime.InteropServices;
using System.Security;

namespace OsHelper.UsbNet.WinUSB
{
	[SuppressUnmanagedCodeSecurity]
	public static class WinUsb
	{
		[DllImport("winusb.dll", EntryPoint = "WinUsb_AbortPipe", SetLastError = true)]
        private static extern bool WinUsb_AbortPipe([In] SafeHandle InterfaceHandle, byte PipeID);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_ControlTransfer", SetLastError = true)]
        private static extern bool WinUsb_ControlTransfer([In] SafeHandle InterfaceHandle,
                                                          [In] UsbSetupPacket SetupPacket,
                                                          IntPtr Buffer,
                                                          int BufferLength,
                                                          out int LengthTransferred,
                                                          IntPtr pOVERLAPPED);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_FlushPipe", SetLastError = true)]
        private static extern bool WinUsb_FlushPipe([In] SafeHandle InterfaceHandle, byte PipeID);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_Free", SetLastError = true)]
        internal static extern bool WinUsb_Free([In] IntPtr InterfaceHandle);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_GetAssociatedInterface", SetLastError = true)]
        internal static extern bool WinUsb_GetAssociatedInterface([In] SafeHandle InterfaceHandle,
                                                                  byte AssociatedInterfaceIndex,
                                                                  ref IntPtr AssociatedInterfaceHandle);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_GetCurrentAlternateSetting", SetLastError = true)]
        internal static extern bool WinUsb_GetCurrentAlternateSetting([In] SafeHandle InterfaceHandle, out byte SettingNumber);


        [DllImport("winusb.dll", EntryPoint = "WinUsb_GetDescriptor", SetLastError = true)]
        private static extern bool WinUsb_GetDescriptor([In] SafeHandle InterfaceHandle,
                                                        byte DescriptorType,
                                                        byte Index,
                                                        ushort LanguageID,
                                                        IntPtr Buffer,
                                                        int BufferLength,
                                                        out int LengthTransferred);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_GetOverlappedResult", SetLastError = true)]
        private static extern bool WinUsb_GetOverlappedResult([In] SafeHandle InterfaceHandle,
                                                              IntPtr pOVERLAPPED,
                                                              out int lpNumberOfBytesTransferred,
                                                              bool Wait);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_GetPipePolicy", SetLastError = true)]
        internal static extern bool WinUsb_GetPipePolicy([In] SafeHandle InterfaceHandle,
                                                         byte PipeID,
                                                         PipePolicyType policyType,
                                                         ref int ValueLength,
                                                         IntPtr Value);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_GetPowerPolicy", SetLastError = true)]
        internal static extern bool WinUsb_GetPowerPolicy([In] SafeHandle InterfaceHandle,
                                                          PowerPolicyType policyType,
                                                          ref int ValueLength,
                                                          IntPtr Value);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_Initialize", SetLastError = true)]
        internal static extern bool WinUsb_Initialize([In] SafeHandle DeviceHandle, [Out, In] ref SafeWinUsbInterfaceHandle InterfaceHandle);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_QueryDeviceInformation", SetLastError = true)]
        internal static extern bool WinUsb_QueryDeviceInformation([In] SafeHandle InterfaceHandle,
                                                                  DeviceInformationTypes InformationType,
                                                                  ref int BufferLength,
                                                                  [MarshalAs(UnmanagedType.AsAny), In, Out] object Buffer);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_QueryInterfaceSettings", SetLastError = true)]
        internal static extern bool WinUsb_QueryInterfaceSettings([In] SafeHandle InterfaceHandle,
                                                                  byte AlternateInterfaceNumber,
                                                                  [MarshalAs(UnmanagedType.LPStruct), In, Out] UsbInterfaceDescriptor
                                                                      UsbAltInterfaceDescriptor);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_QueryPipe", SetLastError = true)]
        internal static extern bool WinUsb_QueryPipe([In] SafeHandle InterfaceHandle,
                                                     byte AlternateInterfaceNumber,
                                                     byte PipeIndex,
                                                     [MarshalAs(UnmanagedType.LPStruct), In, Out] PipeInformation PipeInformation);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_ReadPipe", SetLastError = true)]
        private static extern bool WinUsb_ReadPipe([In] SafeHandle InterfaceHandle,
                                                   byte PipeID,
                                                   Byte[] Buffer,
                                                   int BufferLength,
                                                   out int LengthTransferred,
                                                   IntPtr pOVERLAPPED);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_ReadPipe", SetLastError = true)]
        private static extern bool WinUsb_ReadPipe([In] SafeHandle InterfaceHandle,
                                                   byte PipeID,
                                                   IntPtr pBuffer,
                                                   int BufferLength,
                                                   out int LengthTransferred,
                                                   IntPtr pOVERLAPPED);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_ResetPipe", SetLastError = true)]
        private static extern bool WinUsb_ResetPipe([In] SafeHandle InterfaceHandle, byte PipeID);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_SetPipePolicy", SetLastError = true)]
        internal static extern bool WinUsb_SetPipePolicy([In] SafeHandle InterfaceHandle,
                                                         byte PipeID,
                                                         PipePolicyType policyType,
                                                         int ValueLength,
                                                         IntPtr Value);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_SetPowerPolicy", SetLastError = true)]
        internal static extern bool WinUsb_SetPowerPolicy([In] SafeHandle InterfaceHandle, PowerPolicyType policyType, int ValueLength, IntPtr Value);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_WritePipe", SetLastError = true)]
        private static extern bool WinUsb_WritePipe([In] SafeHandle InterfaceHandle,
                                                    byte PipeID,
                                                    Byte[] Buffer,
                                                    int BufferLength,
                                                    out int LengthTransferred,
                                                    IntPtr pOVERLAPPED);

        [DllImport("winusb.dll", EntryPoint = "WinUsb_WritePipe", SetLastError = true)]
        private static extern bool WinUsb_WritePipe([In] SafeHandle InterfaceHandle,
                                                    byte PipeID,
                                                    IntPtr pBuffer,
                                                    int BufferLength,
                                                    out int LengthTransferred,
                                                    IntPtr pOVERLAPPED);


        public override bool AbortPipe(SafeHandle InterfaceHandle, byte PipeID)
		{
			return WinUsb_AbortPipe(InterfaceHandle, PipeID);
		}

        public override bool ControlTransfer(SafeHandle InterfaceHandle,
                                             UsbSetupPacket SetupPacket,
                                             IntPtr Buffer,
                                             int BufferLength,
                                             out int LengthTransferred)
		{
			return WinUsb_ControlTransfer(InterfaceHandle,
			                              SetupPacket,
			                              Buffer,
			                              BufferLength,
			                              out LengthTransferred,
			                              IntPtr.Zero);
		}

        public override bool FlushPipe(SafeHandle InterfaceHandle, byte PipeID)
		{
			return WinUsb_FlushPipe(InterfaceHandle, PipeID);
		}

        public override bool GetDescriptor(SafeHandle InterfaceHandle,
                                           byte DescriptorType,
                                           byte Index,
                                           ushort LanguageID,
                                           IntPtr Buffer,
                                           int BufferLength,
                                           out int LengthTransferred)
		{
			return WinUsb_GetDescriptor(InterfaceHandle,
			                            DescriptorType,
			                            Index,
			                            LanguageID,
			                            Buffer,
			                            BufferLength,
			                            out LengthTransferred);
		}

        public override bool GetOverlappedResult(SafeHandle InterfaceHandle,
		                                         IntPtr pOVERLAPPED,
		                                         out int numberOfBytesTransferred,
		                                         bool Wait)
		{
			return WinUsb_GetOverlappedResult(InterfaceHandle,
			                                  pOVERLAPPED,
			                                  out numberOfBytesTransferred,
			                                  Wait);
		}

        //public override bool ReadPipe(UsbEndpointBase endPointBase,
        //                              byte[] Buffer,
        //                              int BufferLength,
        //                              out int LengthTransferred,
        //                              int isoPacketSize,
        //                              IntPtr pOVERLAPPED) { return WinUsb_ReadPipe(endPointBase.Device.Handle, endPointBase.EpNum, Buffer, BufferLength, out LengthTransferred, pOVERLAPPED); }

        public override bool ReadPipe(UsbEndpointBase endPointBase,
                                      IntPtr pBuffer,
                                      int BufferLength,
                                      out int LengthTransferred,
                                      int isoPacketSize,
                                      IntPtr pOVERLAPPED)
		{
			return WinUsb_ReadPipe(endPointBase.Device.Handle,
			                       endPointBase.EpNum,
			                       pBuffer,
			                       BufferLength,
			                       out LengthTransferred,
			                       pOVERLAPPED);
		}

        public override bool ResetPipe(SafeHandle InterfaceHandle, byte PipeID)
		{
			return WinUsb_ResetPipe(InterfaceHandle, PipeID);
		}

        //public override bool WritePipe(UsbEndpointBase endPointBase,
        //                               byte[] Buffer,
        //                               int BufferLength,
        //                               out int LengthTransferred,
        //                               int isoPacketSize,
        //                               IntPtr pOVERLAPPED) { return WinUsb_WritePipe(endPointBase.Device.Handle, endPointBase.EpNum, Buffer, BufferLength, out LengthTransferred, pOVERLAPPED); }

        public override bool WritePipe(UsbEndpointBase endPointBase,
                                       IntPtr pBuffer,
                                       int BufferLength,
                                       out int LengthTransferred,
                                       int isoPacketSize,
                                       IntPtr pOVERLAPPED)
		{
			return WinUsb_WritePipe(endPointBase.Device.Handle,
			                        endPointBase.EpNum,
			                        pBuffer,
			                        BufferLength,
			                        out LengthTransferred,
			                        pOVERLAPPED);
		}

        internal static bool OpenDevice(out SafeFileHandle sfhDevice, string DevicePath)
        {
            sfhDevice =
                Kernel32.CreateFile(DevicePath,
                                    NativeFileAccess.FILE_GENERIC_WRITE | NativeFileAccess.FILE_GENERIC_READ,
                                    NativeFileShare.FILE_SHARE_WRITE | NativeFileShare.FILE_SHARE_READ,
                                    IntPtr.Zero,
                                    NativeFileMode.OPEN_EXISTING,
                                    NativeFileFlag.FILE_ATTRIBUTE_NORMAL | NativeFileFlag.FILE_FLAG_OVERLAPPED,
                                    IntPtr.Zero);

            return (!sfhDevice.IsInvalid && !sfhDevice.IsClosed);
		}
		
	}
}

